/**
 * HttpClientUtil.java<br>
 *
 * @author aaronwong.sd@foxmail.com
 * @version 下午3:23:55
 */
package com.mc.httpclient;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonObject;
import com.mc.util.PropertyUtils;

/**
 * http工具类
 *
 * @author aaronwong.sd@foxmail.com
 */
public class HttpClientUtil implements IHttpUtil {
	/** logger */
	private static final Logger			logger		= LoggerFactory.getLogger(HttpClientUtil.class);
	private static final HttpClientUtil	instance	= new HttpClientUtil();
	private final HttpClient			client		= HttpClient.getInstance();
	private final PropertyUtils			config		= PropertyUtils.getInstance();

	private final String				charset;

	public static HttpClientUtil getInstance() {
		return instance;
	}

	private HttpClientUtil() {
		this.charset = this.config.getStringValue("httpclient", "charset");
		logger.info("### init succeed!");
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see com.wctm.api.http.IHttpUtil#sendPost(java.lang.String,
	 * java.lang.String, java.lang.String)
	 */
	@Override
	public Response sendPost(String url, String data, String sequence) throws Exception {
		String retStr = "{}";
		Response respObj = new Response();
		long begin = System.currentTimeMillis();
		CloseableHttpResponse response = null;
		try {
			StringBuffer logbuf = new StringBuffer();
			logbuf.append(sequence == null ? "" : sequence);
			logbuf.append(" send_post_to : ");
			logbuf.append(url);
			logbuf.append("; ");
			logbuf.append("send_data : ");
			logbuf.append(data);
			logger.info(logbuf.toString());

			HttpPost httpPost = new HttpPost(url);
			HttpEntity entity = new StringEntity(data, this.charset);
			httpPost.setEntity(entity);
			response = this.client.execute(httpPost);
			StatusLine statusLine = response.getStatusLine();
			respObj.setStatus(statusLine.getStatusCode());
			if (HttpStatus.SC_OK == statusLine.getStatusCode()) {
				HttpEntity re = response.getEntity();
				retStr = EntityUtils.toString(re, this.charset);
				retStr = retStr == null ? "" : retStr.trim();
			} else {
				JsonObject respJson = this.getJsonerrcode(statusLine);
				respJson.addProperty("sequence", sequence == null ? "" : sequence);
				retStr = respJson.toString();
			}
			respObj.setDataStr(retStr);
			logbuf = new StringBuffer();
			logbuf.append(sequence == null ? "" : sequence);
			logbuf.append(" response_status : ");
			logbuf.append(statusLine.getStatusCode());
			logbuf.append("; ");
			logbuf.append("consuming_time : ");
			logbuf.append(System.currentTimeMillis() - begin);
			logbuf.append(" ms; ");
			logbuf.append("recive_data : ");
			logbuf.append(retStr);
			logger.info(logbuf.toString());
		} catch (IOException e) {
			throw new HttpException("HttpClientUtil.sendPost() throw IOException", e);
		} catch (Exception e) {
			logger.error("收发HTTP请求时异常！", e);
		} finally {
			try {
				if (response != null) {
					EntityUtils.consume(response.getEntity());
				}
			} catch (IOException e) {
			}
		}

		return respObj;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see com.wctm.api.http.IHttpUtil#sendGet(java.lang.String, java.util.Map,
	 * java.lang.String)
	 */
	@Override
	public Response sendGet(String url, Map<String, String> urlParams, String sequence) throws Exception {
		long begin = System.currentTimeMillis();
		URI uri = null;
		String retStr = "";
		Response respObj = new Response();
		CloseableHttpResponse response = null;
		try {
			URIBuilder builder = new URIBuilder(url);
			if (urlParams != null) {
				for (Entry<String, String> e : urlParams.entrySet()) {
					builder = builder.addParameter(URLEncoder.encode(e.getKey(), this.charset), URLEncoder.encode(e.getValue(), this.charset));
				}
			}
			uri = builder.build();
			StringBuffer logbuf = new StringBuffer();
			logbuf.append(sequence == null ? "" : sequence);
			logbuf.append(" send_get_to : ");
			logbuf.append(uri.toString());
			logger.info(logbuf.toString());

			HttpGet httpGet = new HttpGet(url);
			response = this.client.execute(httpGet);
			StatusLine statusLine = response.getStatusLine();
			respObj.setStatus(statusLine.getStatusCode());
			if (HttpStatus.SC_OK == statusLine.getStatusCode()) {
				HttpEntity entity = response.getEntity();
				retStr = EntityUtils.toString(entity, this.charset);
				retStr = retStr == null ? "" : retStr.trim();
			} else {
				JsonObject respJson = this.getJsonerrcode(statusLine);
				respJson.addProperty("sequence", sequence == null ? "" : sequence);
				retStr = respJson.toString();
			}
			respObj.setDataStr(retStr);
			logbuf = new StringBuffer();
			logbuf.append(sequence == null ? "" : sequence);
			logbuf.append(" response_status : ");
			logbuf.append(statusLine.getStatusCode());
			logbuf.append("; ");
			logbuf.append("consuming_time : ");
			logbuf.append(System.currentTimeMillis() - begin);
			logbuf.append(" ms; ");
			logbuf.append("recive_data : ");
			logbuf.append(retStr);
			logger.info(logbuf.toString());
		} catch (IOException e) {
			throw new HttpException("HttpClientUtil.sendGet() throw IOException", e);
		} catch (Exception e) {
			logger.error("收发HTTP请求时异常！", e);
		} finally {
			try {
				if (response != null) {
					EntityUtils.consume(response.getEntity());
				}
			} catch (IOException e) {
			}
		}
		return respObj;
	}

	/**
	 * 获得{"errorCode":"status"}样式的错误码
	 *
	 * @param statusLine
	 * @return 返回json字符串
	 */
	public JsonObject getJsonerrcode(StatusLine statusLine) {
		JsonObject ret = new JsonObject();
		ret.addProperty("errorCode", statusLine.getStatusCode());
		ret.addProperty("errorMesg", statusLine.getReasonPhrase());
		return ret;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see com.iwechat.httpclient.IHttpUtil#upload(java.lang.String,
	 * java.io.File)
	 */
	@Override
	public String upload(String url, File file) throws HttpException {
		String retStr;
		HttpPost httppost = new HttpPost(url);
		if (file != null && file.exists()) {
			FileEntity bin = new FileEntity(file);
			httppost.setEntity(bin);
		}

		logger.info("executing request " + httppost.getRequestLine());
		CloseableHttpResponse response = null;
		try {
			response = this.client.execute(httppost);
			StatusLine statusLine = response.getStatusLine();
			if (HttpStatus.SC_OK == statusLine.getStatusCode()) {
				HttpEntity entity = response.getEntity();
				retStr = EntityUtils.toString(entity, this.charset);
				retStr = retStr == null ? "" : retStr.trim();
			} else {
				JsonObject respJson = this.getJsonerrcode(statusLine);
				retStr = respJson.toString();
			}
		} catch (IOException e) {
			throw new HttpException("HttpClientUtil.upload() throw IOException", e);
		} catch (Exception e) {
			logger.error("收发HTTP请求时异常！", e);
		} finally {
			try {
				if (response != null) {
					EntityUtils.consume(response.getEntity());
				}
			} catch (IOException e) {
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see com.iwechat.httpclient.IHttpUtil#download(java.lang.String)
	 */
	@Override
	public Attachment download(String url) throws HttpException {
		Attachment att = new Attachment();
		String retStr = "";
		CloseableHttpResponse response = null;
		try {
			HttpGet httpGet = new HttpGet(url);
			response = this.client.execute(httpGet);
			StatusLine statusLine = response.getStatusLine();
			if (HttpStatus.SC_OK == statusLine.getStatusCode()) {
				HttpEntity entity = response.getEntity();
				if (entity instanceof StringEntity) {
					retStr = EntityUtils.toString(entity, this.charset);
					retStr = retStr == null ? "" : retStr.trim();
					att.setError(retStr);
				} else if (entity.isStreaming()) {
					String fullName = this.getFileName(response);
					String relName;
					String suffix;
					int index = fullName.lastIndexOf(".");
					if (index > 0) {
						relName = fullName.substring(0, index);
						suffix = fullName.substring(relName.length() + 1);
					} else {
						relName = fullName;
						suffix = "";
					}
					att.setContentLength(entity.getContentLength());
					att.setContentType(entity.getContentType().getValue());
					att.setFileStream(new BufferedInputStream(entity.getContent()));
					att.setFullName(fullName);
					att.setFileName(relName);
					att.setSuffix(suffix);
				}
			} else {
				JsonObject respJson = this.getJsonerrcode(statusLine);
				retStr = respJson.toString();
				att.setError(retStr);
			}
		} catch (IOException e) {
			throw new HttpException("HttpClientUtil.download() throw IOException", e);
		} catch (Exception e) {
			logger.error("收发HTTP请求时异常！", e);
		} finally {
			try {
				if (response != null) {
					EntityUtils.consume(response.getEntity());
				}
			} catch (IOException e) {
			}
		}
		return att;
	}

	/**
	 * 获取response header中Content-Disposition中的filename值
	 *
	 * @param response
	 * @return
	 */
	private String getFileName(HttpResponse response) {
		Header contentHeader = response.getFirstHeader("Content-disposition");
		String filename = null;
		if (contentHeader != null) {
			HeaderElement[] values = contentHeader.getElements();
			if (values.length == 1) {
				NameValuePair param = values[0].getParameterByName("filename");
				if (param != null) {
					filename = param.getValue();
				}
			}
		} else {
			filename = String.valueOf(System.currentTimeMillis());
		}
		return filename;
	}
}
